home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / MacHacksBug / Python 1.5.2c1 / Mac / scripts / MkDistr.py < prev    next >
Encoding:
Python Source  |  2000-06-23  |  6.9 KB  |  314 lines

  1. #
  2. # Interactively decide what to distribute
  3. #
  4. #
  5. # The exclude file signals files to always exclude,
  6. # The pattern file lines are of the form
  7. # *.c
  8. # This excludes all files ending in .c.
  9. #
  10. # The include file signals files and directories to include.
  11. # Records are of the form
  12. # ('Tools:bgen:AE:AppleEvents.py', 'Lib:MacToolbox:AppleEvents.py')
  13. # This includes the specified file, putting it in the given place, or
  14. # ('Tools:bgen:AE:AppleEvents.py', None)
  15. # This excludes the specified file.
  16. #
  17. from MkDistr_ui import *
  18. import fnmatch
  19. import regex
  20. import os
  21. import sys
  22. import macfs
  23. import macostools
  24.  
  25. DEBUG=0
  26.  
  27. SyntaxError='Include/exclude file syntax error'
  28.  
  29. class Matcher:
  30.     """Include/exclude database, common code"""
  31.     
  32.     def __init__(self, filename):
  33.         self.filename = filename
  34.         self.rawdata = []
  35.         self.parse(filename)
  36.         self.rawdata.sort()
  37.         self.rebuild()
  38.         self.modified = 0
  39.  
  40.     def parse(self, dbfile):
  41.         try:
  42.             fp = open(dbfile)
  43.         except IOError:
  44.             return
  45.         data = fp.readlines()
  46.         fp.close()
  47.         for d in data:
  48.             d = d[:-1]
  49.             if not d or d[0] == '#': continue
  50.             pat = self.parseline(d)
  51.             self.rawdata.append(pat)
  52.                 
  53.     def save(self):
  54.         fp = open(self.filename, 'w')
  55.         self.savedata(fp, self.rawdata)
  56.         self.modified = 0
  57.             
  58.     def add(self, value):
  59.         if len(value) == 1:
  60.             value = value + ('',)
  61.         self.rawdata.append(value)
  62.         self.rebuild1(value)
  63.         self.modified = 1
  64.         
  65.     def delete(self, value):
  66.         key = value
  67.         for i in range(len(self.rawdata)):
  68.             if self.rawdata[i][0] == key:
  69.                 del self.rawdata[i]
  70.                 self.unrebuild1(i, key)
  71.                 self.modified = 1
  72.                 return
  73.         print 'Not found!', key
  74.                 
  75.     def getall(self):
  76.         return map(lambda x: x[0], self.rawdata)
  77.     
  78.     def get(self, value):
  79.         for src, dst in self.rawdata:
  80.             if src == value:
  81.                 return src, dst
  82.         print 'Not found!', value
  83.                 
  84.     def is_modified(self):
  85.         return self.modified
  86.                             
  87. class IncMatcher(Matcher):
  88.     """Include filename database and matching engine"""
  89.  
  90.     def rebuild(self):
  91.         self.idict = {}
  92.         self.edict = {}
  93.         for v in self.rawdata:
  94.             self.rebuild1(v)
  95.             
  96.     def parseline(self, line):
  97.         try:
  98.             data = eval(line)
  99.         except:
  100.             raise SyntaxError, line
  101.         if type(data) <> type(()) or len(data) not in (1,2):
  102.             raise SyntaxError, line
  103.         if len(data) == 1:
  104.             data = data + ('',)
  105.         return data
  106.         
  107.     def savedata(self, fp, data):
  108.         for d in self.rawdata:
  109.             fp.write(`d`+'\n')
  110.         
  111.     def rebuild1(self, (src, dst)):
  112.         if dst == '':
  113.             dst = src
  114.         if dst == None:
  115.             self.edict[src] = None
  116.         else:
  117.             self.idict[src] = dst
  118.             
  119.     def unrebuild1(self, num, src):
  120.         if self.idict.has_key(src):
  121.             del self.idict[src]
  122.         else:
  123.             del self.edict[src]
  124.     
  125.     def match(self, patharg):
  126.         removed = []
  127.         # First check the include directory
  128.         path = patharg
  129.         while 1:
  130.             if self.idict.has_key(path):
  131.                 # We know of this path (or initial piece of path)
  132.                 dstpath = self.idict[path]
  133.                 # We do want it distributed. Tack on the tail.
  134.                 while removed:
  135.                     dstpath = os.path.join(dstpath, removed[0])
  136.                     removed = removed[1:]
  137.                 # Finally, if the resultant string ends in a separator
  138.                 # tack on our input filename
  139.                 if dstpath[-1] == os.sep:
  140.                     dir, file = os.path.split(path)
  141.                     dstpath = os.path.join(dstpath, file)
  142.                 if DEBUG:
  143.                     print 'include', patharg, dstpath
  144.                 return dstpath
  145.             path, lastcomp = os.path.split(path)
  146.             if not path:
  147.                 break
  148.             removed[0:0] = [lastcomp]
  149.         # Next check the exclude directory
  150.         path = patharg
  151.         while 1:
  152.             if self.edict.has_key(path):
  153.                 if DEBUG:
  154.                     print 'exclude', patharg, path
  155.                 return ''
  156.             path, lastcomp = os.path.split(path)
  157.             if not path:
  158.                 break
  159.             removed[0:0] = [lastcomp]
  160.         if DEBUG:
  161.             print 'nomatch', patharg
  162.         return None
  163.             
  164.     def checksourcetree(self):
  165.         rv = []
  166.         for name in self.idict.keys():
  167.             if not os.path.exists(name):
  168.                 rv.append(name)
  169.         return rv
  170.                 
  171. class ExcMatcher(Matcher):
  172.     """Exclude pattern database and matching engine"""
  173.  
  174.     def rebuild(self):
  175.         self.relist = []
  176.         for v in self.rawdata:
  177.             self.rebuild1(v)
  178.         
  179.     def parseline(self, data):
  180.         return (data, None)
  181.  
  182.     def savedata(self, fp, data):
  183.         for d in self.rawdata:
  184.             fp.write(d[0]+'\n')        
  185.         
  186.     def rebuild1(self, (src, dst)):
  187.         pat = fnmatch.translate(src)
  188.         if DEBUG:
  189.             print 'PATTERN', `src`, 'REGEX', `pat`
  190.         self.relist.append(regex.compile(pat))
  191.         
  192.     def unrebuild1(self, num, src):
  193.         del self.relist[num]
  194.     
  195.     def match(self, path):
  196.         comps = os.path.split(path)
  197.         file = comps[-1]
  198.         for pat in self.relist:
  199.             if pat and pat.match(file) == len(file):
  200.                 if DEBUG:
  201.                     print 'excmatch', file, pat
  202.                 return 1
  203.         return 0        
  204.          
  205.         
  206. class Main:
  207.     """The main program glueing it all together"""
  208.     
  209.     def __init__(self):
  210.         InitUI()
  211.         fss, ok = macfs.GetDirectory('Source directory:')
  212.         if not ok:
  213.             sys.exit(0)
  214.         os.chdir(fss.as_pathname())
  215.         if not os.path.isdir(':Mac:Distributions'):
  216.             os.mkdir(':Mac:Distributions')
  217.         typedist = GetType()
  218.         self.inc = IncMatcher(':Mac:Distributions:%s.include'%typedist)
  219.         self.exc = ExcMatcher(':Mac:Distributions:%s.exclude'%typedist)
  220.         self.ui = MkDistrUI(self)
  221.         self.ui.mainloop()
  222.         
  223.     def check(self):
  224.         return self.checkdir(':', 1)
  225.         
  226.     def checkdir(self, path, istop):
  227.         if DEBUG:
  228.             print 'checkdir', path
  229.         files = os.listdir(path)
  230.         rv = []
  231.         todo = []
  232.         for f in files:
  233.             if DEBUG:
  234.                 print 'checkfile', f
  235.             if self.exc.match(f):
  236.                 if DEBUG:
  237.                     print 'exclude match', f
  238.                 continue
  239.             fullname = os.path.join(path, f)
  240.             if DEBUG:
  241.                 print 'checkpath', fullname
  242.             matchvalue = self.inc.match(fullname)
  243.             if matchvalue == None:
  244.                 if os.path.isdir(fullname):
  245.                     if DEBUG:
  246.                         print 'do dir', fullname
  247.                     todo.append(fullname)
  248.                 else:
  249.                     if DEBUG:
  250.                         print 'include', fullname
  251.                     rv.append(fullname)
  252.             elif DEBUG:
  253.                 print 'badmatch', matchvalue
  254.         for d in todo:
  255.             if len(rv) > 500:
  256.                 if istop:
  257.                     rv.append('... and more ...')
  258.                 return rv
  259.             rv = rv + self.checkdir(d, 0)
  260.         return rv
  261.         
  262.     def run(self, destprefix):
  263.         missing = self.inc.checksourcetree()
  264.         if missing:
  265.             print '==== Missing source files ===='
  266.             for i in missing:
  267.                 print i
  268.             print '==== Fix and retry ===='
  269.             return
  270.         if not self.rundir(':', destprefix, 0):
  271.             return
  272.         self.rundir(':', destprefix, 1)
  273.  
  274.     def rundir(self, path, destprefix, doit):
  275.         files = os.listdir(path)
  276.         todo = []
  277.         rv = 1
  278.         for f in files:
  279.             if self.exc.match(f):
  280.                 continue
  281.             fullname = os.path.join(path, f)
  282.             if os.path.isdir(fullname):
  283.                 todo.append(fullname)
  284.             else:
  285.                 dest = self.inc.match(fullname)
  286.                 if dest == None:
  287.                     print 'Not yet resolved:', fullname
  288.                     rv = 0
  289.                 if dest:
  290.                     if doit:
  291.                         print 'COPY ', fullname
  292.                         print '  -> ', os.path.join(destprefix, dest)
  293.                         try:
  294.                             macostools.copy(fullname, os.path.join(destprefix, dest), 1)
  295.                         except: #DBG
  296.                             print 'cwd', os.getcwd() #DBG
  297.                             print 'fsspec', macfs.FSSpec(fullname) #DBG
  298.                             raise
  299.         for d in todo:
  300.             if not self.rundir(d, destprefix, doit):
  301.                 rv = 0
  302.         return rv
  303.         
  304.     def save(self):
  305.         self.inc.save()
  306.         self.exc.save()
  307.         
  308.     def is_modified(self):
  309.         return self.inc.is_modified() or self.exc.is_modified()
  310.  
  311. if __name__ == '__main__':
  312.     Main()
  313.     
  314.